﻿using System;
using System.Collections.Generic;
using System.Text;
using Vortex.Drawing;
using Vortex.Input;

namespace Vortex.Tutorials.ClippingZEffectAlphaTest {

    ///<summary>This tutorial shows you how to use clipping, Z-Effect and Alpha Test</summary>
    class ClippingZEffectAlphaTest : Game {
        const float DEMO_ZMASK_VALUE = 0.5f;

        Texture _BackgroundTexture;
        Texture _StencilTexture;
        Texture _DropTexture;

        bool _ClippingEnabled;
        bool _ZMaskEnabled;
        int _ZMaskTechnique = 1;

        public ClippingZEffectAlphaTest() {
            Window.Size = new System.Drawing.Size(800, 600);

            //exit handler
            Keyboard.KeyUp += delegate(VirtualKey key) {
                if (key == VirtualKey.Escape) {
                    Terminate();
                }
            };

            //sample how to use anonymous delegates for key handling
            Keyboard.KeyDown += delegate(VirtualKey key) {
                switch (key) {
                    case VirtualKey.C:
                        _ClippingEnabled = !_ClippingEnabled;
                        break;
                    case VirtualKey.Z:
                        _ZMaskEnabled = !_ZMaskEnabled;
                        break;
                    case VirtualKey.D1:
                        _ZMaskTechnique = 1;
                        break;
                    case VirtualKey.D2:
                        _ZMaskTechnique = 2;
                        break;
                    case VirtualKey.D3:
                        _ZMaskTechnique = 3;
                        break;
                    case VirtualKey.D4:
                        _ZMaskTechnique = 4;
                        break;
                    case VirtualKey.D5:
                        _ZMaskTechnique = 5;
                        break;
                    case VirtualKey.D6:
                        _ZMaskTechnique = 6;
                        break;
                }
            };
        }

        protected override void Load () {
            _BackgroundTexture = new Texture(@"{graphics}\rain-drops.jpg");
            _DropTexture = new Texture(@"{graphics}\drop.png");
            _StencilTexture = new Texture(@"{graphics}\stencil.png");
        }

        protected override void Unload () {
            _BackgroundTexture.Dispose();
            _DropTexture.Dispose();
            _StencilTexture.Dispose();
        }

        protected override void Update (GameTime time) {
        }

        protected override void Render (Canvas2D canvas) {
            canvas.DrawSprite(canvas.Region, _BackgroundTexture.ToSprite(), ColorU.White);

            //apply clipping 
            using (canvas <= CreateClipping()) {
                //fill Z mask only if it is enabled
                if (_ZMaskEnabled) {
                    PrepareZMask(canvas);
                }
				canvas.Z = DEMO_ZMASK_VALUE;
                //if z-mask is enabled lets show only pixels with z-value DEMO_ZMASK_VALUE
                using (canvas <= (_ZMaskEnabled ? new ZEffect(TestFunction.Equal, false) : ZEffect.None)) {
                    //draw content
                    DrawDancingDrops(canvas);
                }
            }

            //draw help text
            canvas.DrawTextLayout(SpriteFont.Console, canvas.Region.Deflate(new Vector2(4, 4)), "C - toggle clipping\nZ - toggle z-mask\n1..6 - z-mask techniques\n\nEsc - Exit", TextLayout.Left | TextLayout.Bottom, ColorU.White);
        }

        private void PrepareZMask (Canvas2D canvas) {
            //set all pixel's z-value into 0.0f
            canvas.Clear(0.0f);

			canvas.Z = DEMO_ZMASK_VALUE;
            //requested to write z-value without any tests
            using (canvas <= ZEffect.WriteOnly) {
                //draw line grid for first 3 similar techniques
                if (_ZMaskTechnique < 4) {
                    int step = _ZMaskTechnique + 1;
                    for (int n = 0; n < Window.Size.Height; n += step) {
                        canvas.DrawLine(0, n, Window.Size.Width, n, ColorU.Invisible);
                    }
                    for (int n = 0; n < Window.Size.Width; n += step) {
                        canvas.DrawLine(n, 0, n, Window.Size.Height, ColorU.Invisible);
                    }
                }
                //draw rectangles for last 2 techniques 
                if (_ZMaskTechnique >= 4 && _ZMaskTechnique <= 5) {
                    for (int n = 0; n < Window.Size.Height; n += 16) {
                        for (int m = 0; m < Window.Size.Width; m += 16) {
                            if (_ZMaskTechnique == 4) {
                                canvas.DrawRect(m, n, 8, 8, ColorU.Invisible);
                            } else {
                                canvas.DrawFilledRect(m, n, 8, 8, ColorU.Invisible);
                            }
                        }
                    }
                }
                //last techinique requires to use alpha test
                if (_ZMaskTechnique == 6) {
                    //we need to write only pixels with alpha > 50%
                    using (canvas <= AlphaTest.PassIfGreater(0.5f)) {
                        canvas.DrawSprite(Window.Size.Width / 2, Window.Size.Height / 2, _StencilTexture.ToSprite(), ColorU.Invisible);
                    }
                }
            }
        }

        ///<summary>Creates clipping with center at mouse point or no clipping if clipping is not enabled</summary>
        private Clipping CreateClipping () {
            return _ClippingEnabled ? new Clipping(Rect.FromPoint(Mouse.Location.X, Mouse.Location.Y, 400, 300)) : Clipping.None;
        }

        ///<summary>Draws drops placed by Sin/Cos funcs</summary>
        private void DrawDancingDrops (Canvas2D canvas) {
            Sprite dropSprite = _DropTexture.ToSprite();
            for (int n = 30; n < 830; n+= 80 ) {
                Vector2 position1 = new Vector2(n, 300 + 200 * (float)Math.Sin(2 * Time.TotalGameTime + n * 0.01f));
                Vector2 position2 = new Vector2(n + 27, 300 + 200 * (float)Math.Sin(2.08 + 2 * Time.TotalGameTime + n * 0.01f));
                Vector2 position3 = new Vector2(n + 54, 300 + 200 * (float)Math.Sin(4.16 + 2 *Time.TotalGameTime + n * 0.01f));
                canvas.DrawSprite(position1, dropSprite, ColorU.White);
                canvas.DrawSprite(position2, dropSprite, ColorU.White);
                canvas.DrawSprite(position3, dropSprite, ColorU.White);
            }
        }
    }
}
